#pragma link "cgauges"
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Globals.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
}

//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::vmLabelsMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
  vmLabels->Hint = "\"" + IntToStr( vmLabels->Items->Count ) + "\" script pointer(s).";
}

//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::vmPointersMouseMove(TObject *Sender, TShiftState Shift, int X, int Y)
{
  vmPointers->Hint = "\"" + IntToStr( vmPointers->Items->Count ) + "\" text pointer(s).";
}

//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::FormCreate(TObject *Sender)
{ // Get the working directory
  getcwd( dirc, MAXPATH );

  // Memory for RAM
  ram = ( BYTE *)malloc( 0x20000 );
  if ( !ram )
  {
    ShowMessage( "Fatal Error: Could not allocate needed memory for RAM." );
    Close( );
  }

  //
  vmText->Clear( );
  vmPointers->Clear( );
  vmLabels->Clear( );  
  vms.Pointer = NULL;

  isRunning = false;  
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  _free( rom.data );
  _free( ram );
  exit( 0 );
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::Dumpallscriptstotextfiles1Click(TObject *Sender)
{
  TVMS TEMP;
  FILE *tf;

  int  index;
  char FileName[512];
  char buf[256];

  if ( !rom.data ) return;
  //
  TEMP  = vms;
  index = -1;
  // TRY to run current script
  vmText->Lines->BeginUpdate( );
  vmText->Clear( );
  // Clear label refs
  memset( vms.LabelRef, 0, (rom.Length - rom.Header) );
  //
  CGBar->MaxValue = vmLabels->Items->Count;
  CGBar->Progress = 0;
  // Open the file to dump to
  tf = fopen( "FS2_Dump.txt", "wt" );
  if ( !tf )
  {
    ShowMessage( "Error: Could not create output file." );
    fcloseall( );
    return;
  }
  //
  while( ++index < vmLabels->Items->Count )
  { //---------------------//
    // Get the pointer nfo //
    //---------------------//
    strcpy( buf, vmLabels->Items->Strings[ index ].c_str( ) );
    sscanf( buf, "%X", &vms.Start );
    //--------------------------
    vms.Start      = Snes2Pc( vms.Start );
    //
    if ( !(vms.LabelRef[ vms.Start ] & 0x80) )
      DecodeTextData( vms.Start, tf );
    else
//      vmText->Lines->Add( ".DUPLUCATE _Label_" + IntToHex( vms.Start, 0 ) );
      fprintf( tf, ".DUPLUCATE _Label_%08X", vms.Start );
    //
    vms.LabelRef[ vms.Start ] |= 0x80;
    //
    CGBar->Progress = index;
    CheckMSG( );
  }

  // Dump the text
  ShowMessage( "Complete! Dump Text data to \"FS2_Dump.txt\"." );
  fcloseall( );
  vmText->Lines->EndUpdate( );
  vms             = TEMP;
  CGBar->Progress = 0;
}


//------------------------------//
//                              //
//------------------------------////------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::OpenROMfile1Click(TObject *Sender)
{
  FILE *rf;

  FOpen1->InitialDir = dirc;
  FOpen1->FileName   = "";
  FOpen1->Filter     = "Snes ROM File|*.s?c;*.fig";
  //
  if ( FOpen1->Execute( ) )
  { //
    strcpy( romf, FOpen1->FileName.c_str( ) );
    rf = fopen( romf, "rb" );
    if ( !rf )
    {
      ShowMessage( "Error: Could not open the ROM file." );
      fcloseall( );
      return;
    }
    //
    _free( rom.data );
    rom.Length   = FileSize( rf );
    rom.Header   = ( rom.Length & 0x0200 );
    rom.data     = ( BYTE *)malloc( ( rom.Length - rom.Header ) );
    vms.LabelRef = ( BYTE *)malloc( ( rom.Length - rom.Header ) << 1 );
    //
    if ( !rom.data || !vms.LabelRef )
    {
      ShowMessage( "Fatal Error: Could not allocate needed memory for ROM file." );
      fcloseall( );
      Close( );
      return;
    }
    //
    fseek( rf, rom.Header, SEEK_SET );
    fread( rom.data, 1, ( rom.Length - rom.Header ), rf );
    memset( vms.LabelRef, 0, (rom.Length - rom.Header) );
    // Test for the correct ROM
    if ( strncmp( &rom.data[0xFFC0], "FARLANDSTORY2", 13 ) )
    { //
      ShowMessage( "Error: This is NOT the correct ROM file." );
      _free( rom.data );
      fcloseall( );
      return;
    }
    //
    fclose( rf );
    //
    OpenTablefile1Click( Sender );
    OpenPointerFile1Click( Sender );    
  }
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::OpenTablefile1Click(TObject *Sender)
{
  char buf[256];
  FILE *rf;

  FOpen1->InitialDir = dirc;
  FOpen1->FileName   = "";
  FOpen1->Filter     = "TABLE File|*.tbl";
  //
  if ( FOpen1->Execute( ) )
  { //
    strcpy( tblf, FOpen1->FileName.c_str( ) );
    rf = fopen( tblf, "rt" );
    if ( !rf )
    {
      ShowMessage( "Error: Could not open the TABLE file." );
      fcloseall( );
      return;
    }

    // Load up table data
    while( fgets( buf, 255, rf ) )
    { //
      strcpy( buf, Trim( AnsiString( buf ) ).c_str( ) );
      //
      if ( buf[2] == '=' )
      { //
        buf[2] = 0;
        strcpy( tbl.sngl[ strtoul( buf, NULL, 16 ) ], &buf[3] );
      }
      else
      if ( buf[4] == '=' )
      { //
        buf[4] = 0;
        strcpy( tbl.dual[ strtoul( buf, NULL, 16 ) ], &buf[5] );
      }
    }
    //
    fclose( rf );
  }
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::OpenPointerFile1Click(TObject *Sender)
{
  char buf[256];
  char arg1[32];
  char arg2[32];
  //
  FILE *rf;

  //------------------------
  if ( !rom.data )
  {
    ShowMessage( "Please load a ROM file before continuing." );
    return;
  }
  //------------------------
  FOpen1->InitialDir = dirc;
  FOpen1->FileName   = "";
  FOpen1->Filter     = "Pointer TEXT File|*.txt";
  //
  if ( FOpen1->Execute( ) )
  { //
    strcpy( txtf, FOpen1->FileName.c_str( ) );
    rf = fopen( txtf, "rt" );
    if ( !rf )
    {
      ShowMessage( "Error: Could not open the TEXT file." );
      fcloseall( );
      return;
    }
    //
    vmPointers->Clear( );
    vmPointers->Items->BeginUpdate( );
    while( fgets( buf, 255, rf ) )
    {
      strcpy( buf, Trim( AnsiString( buf ) ).c_str( ) );
      if ( !buf[0] || buf[0] == ';' || buf[0] == '/' )
        continue;
      //
      memset( arg1, 0, 16 );
      memset( arg2, 0, 16 );
      sscanf( buf, "%s %s", &arg1, &arg2 );
      //
      sprintf( buf, "%s %8c %s", arg1, ' ', arg2 );
      vmPointers->Items->Add( Trim( AnsiString( buf ) ) );
    }
    //
    vmPointers->Items->EndUpdate( );
    //
    vms.curIndex          = 0;
    vmPointers->ItemIndex = 0;
    //
    strcpy( buf, vmPointers->Items->Strings[ vms.curIndex ].c_str( ) );
    sscanf( buf, "%X %X", &vms.Start, &vms.End );
    //--------------------------
    vms.Start      = Snes2Pc( vms.Start );
//      vms.End        = Snes2Pc( vms.End );
    vms.End        = Snes2Pc( rom.Length );
    //
    vms.Pointer    = &rom.data[ vms.Start ];
    vms.BaseOffset = vms.Start;
    // Adjust the offsets to be 'RELATIVE' addresses
    vms.End       -= vms.Start;
    vms.Start      = 0;
    //
    fclose( rf );

    Form1->Caption = "Farland Story 2: Virtual Machine - " + IntToStr( vmPointers->Items->Count ) + " Script pointer(s)";
  }
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::Exit1Click(TObject *Sender)
{
  Close( );
}


//------------------------------//
//                              //
//------------------------------////------------------------------//
//                              //
//------------------------------////------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::vmPointersDblClick(TObject *Sender)
{
  char buf[256];

  if ( !rom.data ) return;
  //
  for ( int c = 0; c < vmPointers->Items->Count; c++ )
  {
    if ( vmPointers->Selected[ c ] )
    { // Clear All sub pointers
      vmText->Clear( );
      vms.curIndex = c;
      // Clear label refs
      memset( vms.LabelRef, 0, (rom.Length - rom.Header) );
      //
      strcpy( buf, vmPointers->Items->Strings[ vms.curIndex ].c_str( ) );
      sscanf( buf, "%X %X", &vms.Start, &vms.End );
      //--------------------------
      vms.Start      = Snes2Pc( vms.Start );
//      vms.End        = Snes2Pc( vms.End );
      vms.End        = Snes2Pc( rom.Length );
      //
      vms.Pointer    = &rom.data[ vms.Start ];
      vms.BaseOffset = vms.Start;
      // Adjust the offsets to be 'RELATIVE' addresses
      vms.End       -= vms.Start;
      vms.Start      = 0;
      //
      break;
    } // END IF
  }
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::FindInternalScripts1Click(TObject *Sender)
{
  FILE *pf,
       *sf;

  TVMS TEMP;

  int  index;
  char FileName[512];
  char buf[256];

  if ( !rom.data ) return;
  //

  TEMP  = vms;
  index = -1;
  // TRY to run current script
  vmLabels->Clear( );

  vmText->Lines->BeginUpdate( );
  vmTextData = "";
  //
  vmText->Clear( );
  vmLabels->Clear( );
  // Clear label refs
  memset( vms.LabelRef, 0, (rom.Length - rom.Header) );
  //
  CGBar->MaxValue = vmPointers->Items->Count;
  CGBar->Progress = 0;
  //
  while( ++index < vmPointers->Items->Count )
  { //
    isRunning = true;
    //---------------------//
    // Get the pointer nfo //
    //---------------------//
    strcpy( buf, vmPointers->Items->Strings[ index ].c_str( ) );
    sscanf( buf, "%X %X", &vms.Start, &vms.End );
    //--------------------------
    vms.Start      = Snes2Pc( vms.Start );
    vms.End        = Snes2Pc( rom.Length );
    //
    vms.LabelRef[ vms.Start ] |= 0x80;
    //
    vms.Pointer    = &rom.data[ vms.Start ];
    vms.BaseOffset = vms.Start;
    // Adjust the offsets to be 'RELATIVE' addresses
    vms.End       -= vms.Start;
    vms.Start      = 0;
    //--------------------------
    //
    while( isRunning )
    {
      if ( VirtuaMachine( ) == -1 ) isRunning = false;
      CheckMSG( );
    }
    //
    CGBar->Progress = index;
  }

  //-------------------------
  index = -1;
  while( ++index < vmPointers->Items->Count )
  { //
    strcpy( buf, vmPointers->Items->Strings[ index ].c_str( ) );
    sscanf( buf, "%X %X", &vms.Start, &vms.End );
    //--------------------------
    vms.Start      = Snes2Pc( vms.Start );
    //--------------------------
    vms.LabelRef[ vms.Start ] |= 0x80;
  }

  //
  vmText->Clear( );
  vmText->Lines->EndUpdate( );
  //
  vms = TEMP;
  CGBar->Progress = 0;
}


//------------------------------//
//                              //
//------------------------------//
void __fastcall TForm1::DecodeTextData( LONG lpOffset, FILE *tf )
{
  LONG posi;
  WORD *Pointer;
  WORD data;
  char buf[64];

  (BYTE *)Pointer = &rom.data[ lpOffset ];

  fprintf( tf, "//***********************//\n"
               "// Kana to be translated //\n"
               "//***********************//\n"
         );
  fprintf( tf, "_Label_%08X:\n ; ", 0xC00000 | lpOffset );
  //-----------------------------//
  //                             //
  //-----------------------------//
  posi = 0;
  data = Pointer[ posi ];
  while( data != 0xFFFF )
  { //---------------//
    // Control Codes //
    //---------------//
    data = Pointer[ posi++ ];
    //
    if ( (data & 0xF000) == 0x8000 )
    { //
      switch( data )
      { // End Text
        case 0x8000: fprintf( tf, "\n"
                                  "\n// Translated text goes here //"
                                  "\n.TRANS\n\n.ENDT\n\n" );
                     data = 0xFFFF;  break;
        // Newline
        case 0x8001: fprintf( tf, "\r\n ; " ); break;
        //
//        case 0x8002:
        //
//        case 0x8003:
        //
//        case 0x8004:
        //
//        case 0x8005:
        // 8x16 text
        case 0x8006: fprintf( tf, "[8x16]" ); break;
        // 16x16 text
        case 0x8007: fprintf( tf, "[16x16]" ); break;
        //
//        case 0x8008:
        //
//        case 0x8009:
        //
//        case 0x800A:
        //
//        case 0x800B:
        // Keypress
        case 0x800C: fprintf( tf, "[Key]" ); break;
        // TextXY
        case 0x800D: fprintf( tf, "[Text: X=%04X, Y=%04X]", Pointer[ posi + 0 ], Pointer[ posi + 1 ] );
                     posi += 2; break;
        //
//        case 0x800E:
        // Clear Screen
        case 0x800F: fprintf( tf, "[Clrs]\r\n ; "); break;
        //--------------------------------
        default    : fprintf( tf, "[$%04X", data ); break;
      }
    }
    else
    { //-----------------------------//
      // Dump table data             //
      //-----------------------------//
      data = ( data >> 8 ) | ( (data & 0xFF) << 8 );
      //
      if ( tbl.dual[ data ][0] )
      { // Dump the text
        fprintf( tf, "%s", tbl.dual[ data ] );
        data = 0;
      }
      else
      {
        fprintf( tf, "[$%04X]", data, 4 );
        data = 0;
      }
    } // END IF
  } // END WHILE
}

/*{
  LONG posi;
  WORD *Pointer;
  WORD data;
  char buf[64];

  (BYTE *)Pointer = &rom.data[ lpOffset ];

  vmText->Text = vmText->Text + AnsiString( "// Kana to be translated //\r\n" );
  vmText->Text = vmText->Text + AnsiString( "_Label_" + IntToHex( 0xC00000 | (lpOffset) , 8 ) + ":\r\n ; " );
  //-----------------------------//
  //                             //
  //-----------------------------//
  posi = 0;
  data = Pointer[ posi ];
  while( data != 0xFFFF )
  { //---------------//
    // Control Codes //
    //---------------//
    data = Pointer[ posi++ ];
    //
    if ( (data & 0xF000) == 0x8000 )
    { //
      switch( data )
      { // End Text
        case 0x8000: vmText->Text = vmText->Text + "\r\n"
                                                   "\r\n// Translated text goes here //"
                                                   "\r\n.TRANS\r\n\r\n.ENDT\r\n\r\n";
                     data = 0xFFFF;  break;
        // Newline
        case 0x8001: vmText->Text = vmText->Text + "\r\n ; "; break;
        //
//        case 0x8002:
        //
//        case 0x8003:
        //
//        case 0x8004:
        //
//        case 0x8005:
        // 8x16 text
        case 0x8006: vmText->Text = vmText->Text + "[8x16]"; break;
        // 16x16 text
        case 0x8007: vmText->Text = vmText->Text + "[16x16]"; break;
        //
//        case 0x8008:
        //
//        case 0x8009:
        //
//        case 0x800A:
        //
//        case 0x800B:
        // Keypress
        case 0x800C: vmText->Text = vmText->Text + "[Key]"; break;
        // TextXY
        case 0x800D: sprintf( buf, "[Text: X=%04X, Y=%04X]", Pointer[ posi + 0 ], Pointer[ posi + 1 ] );
                     vmText->Text = vmText->Text + AnsiString( buf );
                     posi += 2; break;
        //
//        case 0x800E:
        // Clear Screen
        case 0x800F: vmText->Text = vmText->Text + "[Clrs]\r\n ; "; break;
        //--------------------------------
        default    : vmText->Text = vmText->Text + AnsiString( "[$" + IntToHex( data, 4 ) + "]" ); break;
      }
    }
    else
    { //-----------------------------//
      // Dump table data             //
      //-----------------------------//
      data = ( data >> 8 ) | ( (data & 0xFF) << 8 );
      //
      if ( tbl.dual[ data ][0] )
      { // Dump the text
        vmText->Text = vmText->Text + AnsiString( tbl.dual[ data ] );
        data = 0;
      }
      else
      {
        vmText->Text = vmText->Text + AnsiString( "[$" + IntToHex( data, 4 ) + "]" );
        data = 0;
      }
    } // END IF
  } // END WHILE
}
*/

//------------------------------//
//                              //
//------------------------------//
LONG __fastcall TForm1::VirtuaMachine( )
{
  char buf[512];
  BYTE index      = 0;
  LONG value      = 0;
  LONG lpOffset;
  LONG count;

  // If done, leave routine
  if ( vms.Start >= vms.End ) return -1;

  //-----------------------------//
  //                             //
  //-----------------------------//
  vms.opCode = *(WORD *)&vms.Pointer[ vms.Start ];
  vms.Start += 2;
  //
  switch( vms.opCode )
  { //
    case 0xFFFF : //vmTextData = vmTextData + AnsiString( ".ENDCODE[$" + IntToHex( vms.opCode, 4 ) + "]\r\n" );
                  return -1; // End of script
    //---------------------------------//
    // 0 byte codes                    //
    //---------------------------------//
    case 0x0000 : case 0x0001 : case 0x0002 : case 0x0003 :
    case 0x0005 : case 0x0006 : case 0x0007 : case 0x0008 :
    case 0x0009 : case 0x000A : case 0x000B : case 0x000E :
    case 0x000F : case 0x0010 : case 0x0011 : case 0x0012 :
    case 0x0013 : case 0x0015 : case 0x001A : case 0x001B :
    case 0x001C : case 0x001D : case 0x002E : case 0x002F :
    case 0x0030 : case 0x0031 : case 0x0034 : case 0x0035 :
    case 0x0036 : case 0x0037 : case 0x0038 : case 0x0039 :
    case 0x003A : case 0x003B : case 0x0023 : case 0x0045 :
    case 0x0046 : case 0x0047 : case 0x003E : case 0x003F :
    case 0x0040 : break; // 0 bytes
    //---------------------------------//
    // 1 byte codes                    //
    //---------------------------------//
    case 0x0014 : case 0x0016 : case 0x0017 : case 0x0018 :
    case 0x0019 : case 0x0021 : case 0x0022 : case 0x002C :
    case 0x002D : case 0x0032 : case 0x0033 : case 0x0043 :
    case 0x0044 : case 0x0055 :
    case 0x0056 : vms.Start += 1; break; // 1 byte
    //---------------------------------//
    // 2 byte codes                    //
    //---------------------------------//
    case 0x0004 : case 0x000C : case 0x000D : case 0x001E :
    case 0x0024 : case 0x0025 :
    case 0x002A :
    case 0x002B : vms.Start += 2; break; // 2 bytes

    //---------------------------------//
    // 3 byte codes                    //
    //---------------------------------//
    case 0x0028 : // 3 BYTES
    case 0x0029 : vms.Start += 3; break; // 3 bytes
    //---------------------------------//
    // 4 byte codes                    //
    //---------------------------------//
    case 0x001F : // 4 BYTES
    case 0x0020 : vms.Start += 4; break; // 3 bytes
    //---------------- THIS AREA NEEDS WORK ----------------
    case 0x0026 :
    case 0x0027 : while( vms.Pointer[ vms.Start ] != 0xFF && vms.Start < vms.End )
                    vms.Start++;
                  //
                  vms.Start += 2;
                  break;
    //---------------- THIS AREA NEEDS WORK ----------------
    case 0x003C : // Text Pointers, Upper Textbox
    case 0x003D : // Text Pointers, Lower Textbox
    case 0x0041 : value = GET24( vms.Pointer, vms.Start );
//                // Display the text to the form if not there already
//                  if ( !(vms.LabelRef[ Snes2Pc( value ) ] & 0x01) )  DecodeTextData( Snes2Pc( value ) );
                  // Save the offset for a ref to write out a label
                  vmLabels->Items->Add(
                    AnsiString( IntToHex( value = GET24( vms.Pointer, vms.Start ), 8 ) + " [" +
                                IntToHex( (vms.Start + vms.BaseOffset), 8 ) + "]"
                    )
                  );
                  //
                  vms.LabelRef[ Snes2Pc( value ) ] |= 1;
                  vms.Start += 3;
                  break;     // 3 BYTES     ; Upper, lower, Clear Textbox?? @ 24-bit Text pointer
    // Unknown Pointer, record for now
    case 0x0042 : vms.Start += 3; break;     // 3 BYTES     ; unknown?? @ 24-bit pointer
    //---------------------------------
    case 0x0054 : //vmTextData = vmTextData + AnsiString( ".JMP _Script_" + HexStr( vms.Start, 3 ) + "\r\n" );
                  vms.Start += 3; break; // 3 bytes
    //--------------------------------
    case 0x004A : case 0x004B : case 0x004D : case 0x0057 :
    case 0x0058 : case 0x0048 :
    case 0x0049 :
    case 0x004C : vms.Start += 4; break;     // Varies, Could be a "control statement" code
    //--------------------------------
    case 0x004E : // Varies, Could be a "control statement" code
    case 0x004F : vms.Start += 7; break;     //  Varies, Could be a "control statement" code
    //--------------------------------
    case 0x0050 : // Varies, Could be a "control statement" code
    case 0x0051 : vms.Start += 9; break;     // 6 bytes, Varies, Could be a "control statement" code
    //--------------------------------
    case 0x0052 : // Varies, Could be a "control statement" code
    case 0x0053 : vms.Start += 5; break;     // Varies, Could be a "control statement" code
    //--------------------------------
    case 0x0059 : //vmTextData = vmTextData + AnsiString( ".ENDSCRIPT[$" + IntToHex( vms.opCode, 4 ) + "]\r\n" );
                  return -1; // break;
    default     : //vmTextData = vmTextData + AnsiString( ".INVALID OPCODE [$" + IntToHex( vms.opCode, 4 ) + "]\r\n" );
                  break; // 0 bytes - Invald opCode
  }

  return vms.Start;
}






